package com.lj.dict.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.lj.common.utils.CheckUtils;
import com.lj.common.utils.LockUtils;
import com.lj.common.utils.RedisUtil;
import com.lj.dict.constant.DictConstant;
import com.lj.dict.entity.Dict;
import com.lj.dict.mapper.DictMapper;
import com.lj.dict.param.DictPageParam;
import com.lj.dict.param.DictSaveParam;
import com.lj.dict.param.DictUpdateParam;
import com.lj.dict.result.DictInfoResult;
import com.lj.dict.result.DictItemResult;
import com.lj.dict.result.DictPageResult;
import com.lj.dict.service.DictItemService;
import com.lj.dict.service.DictService;
import com.lj.mp.standard.StandardServiceImpl;
import com.lj.mp.utils.PageQueryUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 字典表 服务实现类
 * </p>
 *
 * @author luojing
 * @since 2025-01-10 11:07:47
 */
@Service
public class DictServiceImpl extends StandardServiceImpl<DictMapper, Dict> implements DictService {

    @Resource
    private DictItemService dictItemService;
    @Resource
    private RedisUtil redisUtil;

    @Override
    public IPage<DictPageResult> page(DictPageParam param) {
        return this.page(PageQueryUtils.getPage(param), getQueryWrapper(param)).convert(DictPageResult::of);
    }

    private LambdaQueryWrapper<Dict> getQueryWrapper(DictPageParam param) {
        return lambdaQueryWrapper()
                .like(ObjectUtil.isNotEmpty(param.getDictName()), Dict::getDictName, param.getDictName())
                .eq(ObjectUtil.isNotEmpty(param.getDictType()), Dict::getDictType, param.getDictType())
                ;
    }

    @Override
    public DictInfoResult info(Long id) {
        return DictInfoResult.of(this.getById(id));
    }

    @Override
    public void save(DictSaveParam param) {
        Dict entity = param.toEntity();
        this.save(entity);
    }

    @Override
    public void update(DictUpdateParam param) {
        Dict entity = param.toEntity();
        this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 删除字典项
        dictItemService.deleteByDictIds(ids);
        // 删除字典
        this.removeByIds(ids);
    }

    @Override
    public List<DictItemResult<Object>> getDictItemByName(String dictType) {
        // 先在缓存中查询
        List<DictItemResult<Object>> cacheList = getDictItemFromCache(dictType);
        if (CollUtil.isNotEmpty(cacheList)) {
            return cacheList;
        }
        return LockUtils.lock(DictConstant.DICT_LOCK + dictType, () -> {
            // 这里为什么还要再查询一次缓存? 防止大量请求同一时间进来获取字典信息(毕竟这个接口是一个高频使用的接口),都先判断缓存没有，
            // 然后获取锁,都往缓存中添加一份字典项 就会发现该字典项被加了好几份
            // 这里借鉴了双重自检锁的逻辑
            List<DictItemResult<Object>> resultList = getDictItemFromCache(dictType);
            if (CollUtil.isNotEmpty(resultList)) {
                return resultList;
            }
            Dict dict = this.getByType(dictType);
            CheckUtils.ifNull(dict, "字典类型不存在:" + dictType);
            List<DictItemResult<Object>> dictItem = dictItemService.getDictItem(dict);
            if (redisUtil.listRightPushAll(DictConstant.DICT_TYPE_CACHE + dictType, dictItem)) {
                // 设置一个超时时间
                redisUtil.expire(DictConstant.DICT_TYPE_CACHE + dictType, DictConstant.DICT_TYPE_CACHE_EXPIRE, TimeUnit.MINUTES);
            }
            return dictItem;
        });
    }

    private List<DictItemResult<Object>> getDictItemFromCache(String dictType) {
        return redisUtil.getList(DictConstant.DICT_TYPE_CACHE + dictType);
    }
}
